//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
//-----------------------------------------------------------------------------
// Net IP.C
// This module is the IP layer
// Refer to RFC 791, 1122, and RFC 815 (fragmentation)
//-----------------------------------------------------------------------------
#include "lh7a400_map.h"
#include "LH7A400_drivers.h"
#include "watertek_drivers.h"
#include "net_net.h"
#include "watertek_main.h"

extern UNS_32  my_ipaddr;
WAIT  wait1;

//lcd
extern UNS_16 lcdCurX;
extern UNS_16 lcdCurY;

extern UDP_DATA udp_data;
extern UNS_32 BootupState;

//------------------------------------------------------------------------
// This handles outgoing IP datagrams.  It adds the 20 byte IP header
// and checksum then forwards the IP datagram to the Ethernet layer
// for sending. See "TCP/IP Illustrated, Volume 1" Sect 3.2
//------------------------------------------------------------------------
//static
UNS_16 ip_ident = 0;
void ip_send(UNS_8 *outbuf, UNS_32 ipaddr, UNS_8 proto_id, UNS_16 len)
{
    IP_HEADER  *ip;
    UNS_8  *hwaddr;

    ip = (IP_HEADER  *)(outbuf + ETH_HEADER_LEN);
    ip->ver_len = 0x45;                         // IPv4 with 20 byte header
    ip->type_of_service = 0;
    ip->total_length = INTSWAP( 20 + len );
    ip->identifier = INTSWAP(ip_ident++);       // sequential identifier
    ip->fragment_info = 0;                      // not fragmented
    ip->time_to_live = 255;                      // max hops
    ip->protocol_id = proto_id;                 // type of payload
    ip->header_cksum = 0;
    ip->source_ipaddr = my_ipaddr;
    ip->dest_ipaddr = ipaddr;                   // Outgoing IP address

    // Compute and insert complement of checksum of ip header
    // Outgoing ip header length is always 20 bytes
    ip->header_cksum = ~cksum(outbuf + ETH_HEADER_LEN, IP_HEADER_LEN);

    // Use ARP to get hardware address to send this to
    hwaddr = arp_resolve(ip->dest_ipaddr);

    // Null means that the ARP resolver did not find the IP address
    // in its cache so had to send an ARP request
    if (hwaddr == NULL) {
        // Fill in the destination information so ehrn the ARP response
        // arrives we can identify it and know what to do when we get it
        wait1.buf = outbuf;
        wait1.ipaddr = ip->dest_ipaddr;
        wait1.proto_id = proto_id;
        wait1.len = len;
        wait1.timer = ARP_TIMEOUT;
        return;
    }

    ETH_Send(outbuf, hwaddr, IP_PACKET, 20 + len);
}

//------------------------------------------------------------------------
// This handles incoming IP datagrams from the Ethernet layer
// See "TCP/IP Illustrated, Volume 1" Sect 3.2
//------------------------------------------------------------------------
void ip_rcve(UNS_8 *inbuf)
{
    IP_HEADER  *ip;
    UNS_32  i;
    UNS_16  header_len, payload_len;

    ip = (IP_HEADER  *)(inbuf + ETH_HEADER_LEN);

    // Make sure it is addressed to my IP address
    if (ip->dest_ipaddr != my_ipaddr) {
        DBGMSG("IP  : It's not my ipaddr !    " );
        return;
    }

    // Validate checksum of ip header
    header_len = 4 * (0x0F & ip->ver_len);
    payload_len = INTSWAP(ip->total_length) - header_len;
    if (cksum(inbuf + ETH_HEADER_LEN, header_len) != 0xFFFF) {
        DBGMSG("IP  : Error, cksum bad !      " );
        return;
    }

    // Make sure incoming message is IP version 4
    if ((ip->ver_len >> 4) != 0x04) {
        DBGMSG("IP  : Error, Not IPv4 !       " );
        return;
    }

    // Make sure incoming message is not fragmented because
    // we cannot handle fragmented messages
    if ((ip->fragment_info & 0x3FFF) != 0) {
        DBGMSG("IP  : Fragmented msg rcvd !   " );
        return;
    }

    // At this point we have received a valid IP datagram addressed
    // to me.  We do not use header options, and do not forward
    // messages, so in the unlikely event there are header options,
    // delete them and shift the data down. The advantage is that
    // layers such as UDP and TCP know where their data starts
    if (header_len > 20) {
        DBGMSG("IP  : Rcvd header > 20 bytes !" );
        return;
    }

    // Look at protocol ID byte and call the appropriate
    // function to handle the received message.  See
    // "TCP/IP Illustrated, Volume 1" Sect 1.7 and RFC 791
    // for values for various protocols
    switch (ip->protocol_id) {
        case ICMP_TYPE:
            DBGMSG("IP  : ICMP pkt rcvd !         " );
            icmp_rcve(inbuf, payload_len);
            break;

        case IGMP_TYPE:
        // We cannot handle IGMP messages
            DBGMSG("IP  : IGMP pkt rcvd !         " );
            break;

        case UDP_TYPE:
        // We cannot handle UDP messages
            DBGMSG("IP  : UDP pkt rcvd !          " );{
            if (check_udp((UNS_8 *)ip, &udp_data.dest_port,
                &udp_data.source_port, &udp_data.pdata,
                &udp_data.length) == 0) {
                DBGMSG("UDP : get data!               " );
                if (BootupState == 2)
                    RecvTftpData(&udp_data);
            }
        }
            break;

        case TCP_TYPE:
        // We cannot handle TCP messages
            DBGMSG("IP  : TCP pkt rcvd !          " );
            break;

        default:
            DBGMSG("IP  : Unknown IP proto id rcvd" );
            break;
    }
}
